{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "f705f4be70e9"
   },
   "outputs": [],
   "source": [
    "# Copyright 2025 Google LLC\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6a63f50996b5"
   },
   "source": [
    "# Getting Started with Google A2A (Agent-to-Agent) Communication\n",
    "\n",
    "This notebook introduces you to Google's Agent-to-Agent (A2A) protocol, a standardized way for AI agents to communicate and collaborate.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "d6581a815af6"
   },
   "source": [
    "<table align=\"left\">\n",
    "  <td style=\"text-align: center\">\n",
    "    <a href=\"https://colab.research.google.com/github/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
    "      <img width=\"32px\" src=\"https://www.gstatic.com/pantheon/images/bigquery/welcome_page/colab-logo.svg\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
    "    </a>\n",
    "  </td>\n",
    "  <td style=\"text-align: center\">\n",
    "    <a href=\"https://console.cloud.google.com/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2Fa2aproject%2Fa2a-samples%2Fmain%2Fnotebooks%2Fa2a_quickstart.ipynb\">\n",
    "      <img width=\"32px\" src=\"https://lh3.googleusercontent.com/JmcxdQi-qOpctIvWKgPtrzZdJJK-J3sWE1RsfjZNwshCFgE_9fULcNpuXYTilIR2hjwN\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
    "    </a>\n",
    "  </td>\n",
    "  <td style=\"text-align: center\">\n",
    "    <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/a2aproject/a2a-samples/main/notebooks/a2a_quickstart.ipynb\">\n",
    "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/vertexai/v1/32px.svg\" alt=\"Vertex AI logo\"><br> Open in Vertex AI Workbench\n",
    "    </a>\n",
    "  </td>\n",
    "  <td style=\"text-align: center\">\n",
    "    <a href=\"https://console.cloud.google.com/bigquery/import?url=https://github.com/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
    "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/bigquery/v1/32px.svg\" alt=\"BigQuery Studio logo\"><br> Open in BigQuery Studio\n",
    "    </a>\n",
    "  </td>\n",
    "  <td style=\"text-align: center\">\n",
    "    <a href=\"https://github.com/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
    "      <img width=\"32px\" src=\"https://www.svgrepo.com/download/217753/github.svg\" alt=\"GitHub logo\"><br> View on GitHub\n",
    "    </a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "44f672f23c9c"
   },
   "source": [
    "## What You'll Build\n",
    "\n",
    "A three-agent system that works together to analyze trending topics:\n",
    "1. **Trending Topics Agent** - Searches the web for current trending topics\n",
    "2. **Trend Analyzer Agent** - Performs deep analysis with quantitative data\n",
    "3. **Host Agent** - Orchestrates the other agents to provide comprehensive insights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "15d597d58ac1"
   },
   "source": [
    "<img src=\"https://storage.googleapis.com/github-repo/a2a/a2a-diagram.png\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "f8bb6c6346eb"
   },
   "source": [
    "## Prerequisites\n",
    "\n",
    "- Python 3.11+\n",
    "- Google Cloud Project with Vertex AI enabled\n",
    "- Basic understanding of async Python\n",
    "\n",
    "## Other Resources\n",
    "\n",
    "- [Google ADK Documentation](https://google.github.io/adk-docs/)\n",
    "- [A2A Protocol Specification](https://github.com/google/a2a)\n",
    "- [Vertex AI Documentation](https://cloud.google.com/vertex-ai)\n",
    "- Codelabs:\n",
    "  - [Google's Agent Stack in Action: ADK, A2A, MCP on Google Cloud](https://codelabs.developers.google.com/instavibe-adk-multi-agents/instructions)\n",
    "  - [Getting Started with Agent-to-Agent (A2A) Protocol: Gemini on Cloud Run](https://codelabs.developers.google.com/intro-a2a-purchasing-concierge)\n",
    "  - [Getting Started with MCP, ADK and A2A](https://codelabs.developers.google.com/codelabs/currency-agent)\n",
    "\n",
    "#### Important!\n",
    "A2A is a work in progress (WIP) thus, in the near future there might be changes that are different from what demonstrated in this code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "a689435e04b8"
   },
   "source": [
    "### Setup and Installation\n",
    "\n",
    "First, let's install the required dependencies:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "9b8e4fde0015"
   },
   "outputs": [],
   "source": [
    "# Install required packages\n",
    "%pip install --upgrade -q google-genai google-adk>=1.6.1 a2a-sdk==0.3.0 python-dotenv aiohttp uvicorn requests mermaid-python nest-asyncio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "415604558ae1"
   },
   "source": [
    "## 1. Introduction to A2A\n",
    "\n",
    "### What is Agent-to-Agent (A2A) Communication?\n",
    "\n",
    "A2A is a standardized protocol that enables AI agents to:\n",
    "- **Discover** each other's capabilities\n",
    "- **Communicate** using a common JSON-RPC based protocol\n",
    "- **Collaborate** to solve complex tasks\n",
    "- **Stream** responses for real-time interactions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8991eb966e73"
   },
   "source": [
    "### Environment Configuration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "2c229a99c6b8"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# Set Google Cloud Configuration\n",
    "os.environ[\"GOOGLE_GENAI_USE_VERTEXAI\"] = \"TRUE\"\n",
    "os.environ[\"GOOGLE_CLOUD_PROJECT\"] = (\n",
    "    \"[your-project-id]\"  # @param {type: \"string\", placeholder: \"[your-project-id]\", isTemplate: true}\n",
    ")\n",
    "os.environ[\"GOOGLE_CLOUD_LOCATION\"] = \"global\"  # Replace with your location\n",
    "\n",
    "load_dotenv()\n",
    "\n",
    "print(\"Environment variables configured:\")\n",
    "print(f\"GOOGLE_GENAI_USE_VERTEXAI: {os.environ['GOOGLE_GENAI_USE_VERTEXAI']}\")\n",
    "print(f\"GOOGLE_CLOUD_PROJECT: {os.environ['GOOGLE_CLOUD_PROJECT']}\")\n",
    "print(f\"GOOGLE_CLOUD_LOCATION: {os.environ['GOOGLE_CLOUD_LOCATION']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "8ed891e4daf5"
   },
   "outputs": [],
   "source": [
    "# Authenticate your notebook environment (Colab only)\n",
    "if \"google.colab\" in sys.modules:\n",
    "    from google.colab import auth\n",
    "\n",
    "    auth.authenticate_user(project_id=os.environ[\"GOOGLE_CLOUD_PROJECT\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bf7079208e95"
   },
   "source": [
    "## 2. Building Your A2A System\n",
    "\n",
    "Let's build our three-agent system step by step. We'll create:\n",
    "\n",
    "1. **Trending Topics Agent** - Finds current trending topics\n",
    "2. **Trend Analyzer Agent** - Analyzes trends with quantitative data\n",
    "3. **Host Agent** - Orchestrates the other agents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from a2a.server.apps import A2AStarletteApplication\n",
    "from a2a.server.request_handlers import DefaultRequestHandler\n",
    "from a2a.server.tasks import InMemoryTaskStore\n",
    "from a2a.types import AgentCapabilities, AgentCard, AgentSkill\n",
    "from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH\n",
    "from google.adk.a2a.executor.a2a_agent_executor import (\n",
    "    A2aAgentExecutor,\n",
    "    A2aAgentExecutorConfig,\n",
    ")\n",
    "from google.adk.agents import Agent\n",
    "from google.adk.agents.remote_a2a_agent import RemoteA2aAgent\n",
    "from google.adk.artifacts import InMemoryArtifactService\n",
    "from google.adk.memory.in_memory_memory_service import InMemoryMemoryService\n",
    "from google.adk.runners import Runner\n",
    "from google.adk.sessions import InMemorySessionService\n",
    "from google.adk.tools import google_search"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "e1d2442d987d"
   },
   "source": [
    "### Agent 1: Trending Topics Agent\n",
    "\n",
    "This agent searches the web for trending topics and returns a list of current trends."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "001caeaf844f"
   },
   "outputs": [],
   "source": [
    "# Create the Trending Topics ADK Agent\n",
    "trending_agent = Agent(\n",
    "    model=\"gemini-2.5-pro\",\n",
    "    name=\"trending_topics_agent\",\n",
    "    instruction=\"\"\"\n",
    "    You are a social media trends analyst. Your job is to search the web for current trending topics,\n",
    "    particularly from social platforms.\n",
    "\n",
    "    When asked about trends:\n",
    "    1. Search for \"trending topics today\" or similar queries\n",
    "    2. Extract the top 3 trending topics\n",
    "    3. Return them in a JSON format\n",
    "\n",
    "    Focus on current, real-time trends from the last 24 hours.\n",
    "\n",
    "    You MUST return your response in the following JSON format:\n",
    "    {\n",
    "        \"trends\": [\n",
    "            {\n",
    "                \"topic\": \"Topic name\",\n",
    "                \"description\": \"Brief description (1-2 sentences)\",\n",
    "                \"reason\": \"Why it's trending\"\n",
    "            },\n",
    "            {\n",
    "                \"topic\": \"Topic name\",\n",
    "                \"description\": \"Brief description (1-2 sentences)\", \n",
    "                \"reason\": \"Why it's trending\"\n",
    "            },\n",
    "            {\n",
    "                \"topic\": \"Topic name\",\n",
    "                \"description\": \"Brief description (1-2 sentences)\",\n",
    "                \"reason\": \"Why it's trending\"\n",
    "            }\n",
    "        ]\n",
    "    }\n",
    "\n",
    "    Only return the JSON object, no additional text.\n",
    "    \"\"\",\n",
    "    tools=[google_search],\n",
    ")\n",
    "\n",
    "print(\"Trending Topics Agent created successfully!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trending_agent_card = AgentCard(\n",
    "    name=\"Trending Topics Agent\",\n",
    "    url=\"http://localhost:10020\",\n",
    "    description=\"Searches the web for current trending topics from social media\",\n",
    "    version=\"1.0\",\n",
    "    capabilities=AgentCapabilities(streaming=True),\n",
    "    default_input_modes=[\"text/plain\"],\n",
    "    default_output_modes=[\"application/json\"],\n",
    "    skills=[\n",
    "        AgentSkill(\n",
    "            id=\"find_trends\",\n",
    "            name=\"Find Trending Topics\",\n",
    "            description=\"Searches for current trending topics on social media\",\n",
    "            tags=[\"trends\", \"social media\", \"twitter\", \"current events\"],\n",
    "            examples=[\n",
    "                \"What's trending today?\",\n",
    "                \"Show me current Twitter trends\",\n",
    "                \"What are people talking about on social media?\",\n",
    "            ],\n",
    "        )\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "remote_trending_agent = RemoteA2aAgent(\n",
    "    name=\"find_trends\",\n",
    "    description=\"Searches for current trending topics on social media\",\n",
    "    agent_card=f\"http://localhost:10020{AGENT_CARD_WELL_KNOWN_PATH}\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "f14d4d0b57a4"
   },
   "source": [
    "### Agent 2: Trend Analyzer Agent\n",
    "\n",
    "This agent takes a specific trend and performs deep analysis with quantitative data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "161a348d7104"
   },
   "outputs": [],
   "source": [
    "# Create the Trend Analyzer ADK Agent\n",
    "analyzer_agent = Agent(\n",
    "    model=\"gemini-2.5-pro\",\n",
    "    name=\"trend_analyzer_agent\",\n",
    "    instruction=\"\"\"\n",
    "    You are a data analyst specializing in trend analysis. When given a trending topic,\n",
    "    perform deep research to find quantitative data and insights.\n",
    "\n",
    "    For each trend you analyze:\n",
    "    1. Search for statistics, numbers, and metrics related to the trend\n",
    "    2. Look for:\n",
    "       - Engagement metrics (views, shares, mentions)\n",
    "       - Growth rates and timeline\n",
    "       - Geographic distribution\n",
    "       - Related hashtags or keywords\n",
    "    3. Provide concrete numbers and data points\n",
    "\n",
    "    Keep it somehow concise\n",
    "\n",
    "    Always prioritize quantitative information over qualitative descriptions.\n",
    "    \"\"\",\n",
    "    tools=[google_search],\n",
    ")\n",
    "\n",
    "print(\"Trend Analyzer Agent created successfully!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "analyzer_agent_card = AgentCard(\n",
    "    name=\"Trend Analyzer Agent\",\n",
    "    url=\"http://localhost:10021\",\n",
    "    description=\"Performs deep analysis of trends with quantitative data\",\n",
    "    version=\"1.0\",\n",
    "    capabilities=AgentCapabilities(streaming=True),\n",
    "    default_input_modes=[\"text/plain\"],\n",
    "    default_output_modes=[\"application/json\"],\n",
    "    skills=[\n",
    "        AgentSkill(\n",
    "            id=\"analyze_trend\",\n",
    "            name=\"Analyze Trend\",\n",
    "            description=\"Provides quantitative analysis of a specific trend\",\n",
    "            tags=[\"analysis\", \"data\", \"metrics\", \"statistics\"],\n",
    "            examples=[\n",
    "                \"Analyze the #ClimateChange trend\",\n",
    "                \"Get metrics for the Taylor Swift trend\",\n",
    "                \"Provide data analysis for AI adoption trend\",\n",
    "            ],\n",
    "        )\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "remote_analyzer_agent = RemoteA2aAgent(\n",
    "    name=\"analyze_trend\",\n",
    "    description=\"Provides quantitative analysis of a specific trend\",\n",
    "    agent_card=f\"http://localhost:10021{AGENT_CARD_WELL_KNOWN_PATH}\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "18e76145ebd4"
   },
   "source": [
    "### Agent 3: Host Agent (Orchestrator)\n",
    "\n",
    "The Host Agent coordinates between the other two agents to provide comprehensive trend analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "99522ad83421"
   },
   "outputs": [],
   "source": [
    "# Create the Host ADK Agent\n",
    "host_agent = Agent(\n",
    "    model=\"gemini-2.5-pro\",\n",
    "    name=\"trend_analysis_host\",\n",
    "    instruction=\"\"\"\n",
    "You are an expert AI Orchestrator.\n",
    "Your primary responsibility is to intelligently interpret user requests, plan the necessary sequence of actions if multiple steps are involved, and delegate them to the most appropriate specialized remote agents.\n",
    "You do not perform the tasks yourself but manage their assignment, sequence, and can monitor their status.\n",
    "\n",
    "Core Workflow & Decision Making:\n",
    "\n",
    "1.  **Understand User Intent & Complexity:**\n",
    "    *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.\n",
    "    *   **Identify if the request requires a single agent or a sequence of actions from multiple agents.** For example, it could require two agents to be called in sequence.\n",
    "\n",
    "2.  **Agent Discovery & Selection:**\n",
    "    *   You have access to sub_agents with specific capabilities (e.g., what kind of requests each agent is designed to handle and what data they output).\n",
    "    *   Based on the user's intent:\n",
    "        *   For **single-step requests**, select the single most appropriate agent.\n",
    "        *   For **multi-step requests**, identify all necessary agents and determine the logical order of their execution.\n",
    "\n",
    "3.  **Task Planning & Sequencing (for Multi-Step Requests):**\n",
    "    *   Before delegating, outline the sequence of agent tasks.\n",
    "    *   Identify dependencies: Does Agent B need information from Agent A's completed task?\n",
    "    *   Plan to execute tasks sequentially if there are dependencies, waiting for the completion of a prerequisite task before initiating the next one.\n",
    "\n",
    "4.  **Task Delegation & Management:**\n",
    "    *   **For New Single Requests or the First Step in a Sequence:**:\n",
    "        *   The `message` extracted from the user's input, formatted in a way the target agent will understand (check the agent info to better structure the message). \n",
    "    *   **For Subsequent Steps in a Sequence:**\n",
    "        *   Once the prerequisite task is done, gather any necessary output from it.\n",
    "        *   Then, use the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.\n",
    "\n",
    "**Communication with User:**\n",
    "\n",
    "*   Clearly inform the user which agent is handling each task. The user should know the entire sequence of agents you used and the results of each one.\n",
    "*   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, proactively ask the user for clarification.\n",
    "*   Rely strictly on your tools and the information they provide.\n",
    "*   Communicate to the user the content from the data gathered from the all remote agents responses.\n",
    "*   The communication to the user should contain most of the information from the remote agents, do not summarize too much.\n",
    "\n",
    "**Important Reminders:**\n",
    "*   Always prioritize selecting the correct agent(s) based on their documented purpose.\n",
    "*   Ensure all information required by the chosen remote agent is included in the call, including outputs from previous agents if it's a sequential task.\n",
    "*   Focus on the most recent parts of the conversation for immediate context, but maintain awareness of the overall goal, especially for multi-step requests.\n",
    "\n",
    "\"\"\",\n",
    "    sub_agents=[remote_trending_agent, remote_analyzer_agent],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "host_agent_card = AgentCard(\n",
    "    name=\"Trend Analysis Host\",\n",
    "    url=\"http://localhost:10022\",\n",
    "    description=\"Orchestrates trend discovery and analysis using specialized agents\",\n",
    "    version=\"1.0\",\n",
    "    capabilities=AgentCapabilities(streaming=True),\n",
    "    default_input_modes=[\"text/plain\"],\n",
    "    default_output_modes=[\"application/json\"],\n",
    "    skills=[\n",
    "        AgentSkill(\n",
    "            id=\"comprehensive_trend_analysis\",\n",
    "            name=\"Comprehensive Trend Analysis\",\n",
    "            description=\"Finds trending topics and provides deep analysis of the most relevant one\",\n",
    "            tags=[\"trends\", \"analysis\", \"orchestration\", \"insights\"],\n",
    "            examples=[\n",
    "                \"Analyze current trends\",\n",
    "                \"What's trending and why is it important?\",\n",
    "                \"Give me a comprehensive trend report\",\n",
    "            ],\n",
    "        )\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1802fda73322"
   },
   "source": [
    "## 3. Running\n",
    "\n",
    "Now let's put everything together. We'll create helper functions to start our agents and run the complete system."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5b4c7a75c7ec"
   },
   "source": [
    "### Starting the A2A Servers\n",
    "\n",
    "Create function to run each agent as an A2A server:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "60b96fd4e891"
   },
   "outputs": [],
   "source": [
    "def create_agent_a2a_server(agent, agent_card):\n",
    "    \"\"\"Create an A2A server for any ADK agent.\n",
    "\n",
    "    Args:\n",
    "        agent: The ADK agent instance\n",
    "        agent_card: The ADK agent card\n",
    "\n",
    "    Returns:\n",
    "        A2AStarletteApplication instance\n",
    "    \"\"\"\n",
    "    runner = Runner(\n",
    "        app_name=agent.name,\n",
    "        agent=agent,\n",
    "        artifact_service=InMemoryArtifactService(),\n",
    "        session_service=InMemorySessionService(),\n",
    "        memory_service=InMemoryMemoryService(),\n",
    "    )\n",
    "\n",
    "    config = A2aAgentExecutorConfig()\n",
    "    executor = A2aAgentExecutor(runner=runner, config=config)\n",
    "\n",
    "    request_handler = DefaultRequestHandler(\n",
    "        agent_executor=executor,\n",
    "        task_store=InMemoryTaskStore(),\n",
    "    )\n",
    "\n",
    "    # Create A2A application\n",
    "    return A2AStarletteApplication(agent_card=agent_card, http_handler=request_handler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import asyncio\n",
    "import threading\n",
    "import time\n",
    "\n",
    "import nest_asyncio\n",
    "import uvicorn\n",
    "\n",
    "# Apply nest_asyncio to allow nested event loops in Jupyter\n",
    "nest_asyncio.apply()\n",
    "\n",
    "# Global servers to keep references\n",
    "servers = []\n",
    "\n",
    "\n",
    "async def run_server_notebook(create_agent_function, port):\n",
    "    \"\"\"Run server with proper error handling.\"\"\"\n",
    "    try:\n",
    "        print(f\"🚀 Starting agent on port {port}...\")\n",
    "        app = create_agent_function()\n",
    "        config = uvicorn.Config(\n",
    "            app.build(), host=\"127.0.0.1\", port=port, log_level=\"error\", loop=\"asyncio\"\n",
    "        )\n",
    "        server = uvicorn.Server(config)\n",
    "        servers.append(server)\n",
    "        await server.serve()\n",
    "    except Exception as e:\n",
    "        print(f\"Agent error: {e}\")\n",
    "\n",
    "\n",
    "def run_agent_in_background(create_agent_function, port, name):\n",
    "    \"\"\"Run an agent server in a background thread.\"\"\"\n",
    "\n",
    "    def run():\n",
    "        loop = asyncio.new_event_loop()\n",
    "        asyncio.set_event_loop(loop)\n",
    "        try:\n",
    "            # Create the coroutine inside the new event loop\n",
    "            loop.run_until_complete(run_server_notebook(create_agent_function, port))\n",
    "        except Exception as e:\n",
    "            print(f\"{name} error: {e}\")\n",
    "\n",
    "    thread = threading.Thread(target=run, daemon=True)\n",
    "    thread.start()\n",
    "    return thread\n",
    "\n",
    "\n",
    "# Start agent servers with corrected function calls\n",
    "print(\"Starting agent servers...\\n\")\n",
    "\n",
    "trending_thread = run_agent_in_background(\n",
    "    lambda: create_agent_a2a_server(trending_agent, trending_agent_card),\n",
    "    10020,\n",
    "    \"Trending Agent\",\n",
    ")\n",
    "analyzer_thread = run_agent_in_background(\n",
    "    lambda: create_agent_a2a_server(analyzer_agent, analyzer_agent_card),\n",
    "    10021,\n",
    "    \"Analyzer Agent\",\n",
    ")\n",
    "host_thread = run_agent_in_background(\n",
    "    lambda: create_agent_a2a_server(host_agent, host_agent_card), 10022, \"Host Agent\"\n",
    ")\n",
    "\n",
    "# Wait for servers to start\n",
    "time.sleep(3)\n",
    "\n",
    "# Check if threads are alive\n",
    "if trending_thread.is_alive() and analyzer_thread.is_alive():\n",
    "    print(\"\\n✅ Agent servers are running!\")\n",
    "    print(\"   - Trending Agent: http://127.0.0.1:10020\")\n",
    "    print(\"   - Analyzer Agent: http://127.0.0.1:10021\")\n",
    "    print(\"   - Host Agent: http://127.0.0.1:10022\")\n",
    "else:\n",
    "    print(\"\\n❌ Agent servers failed to start. Check the error messages above.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8211c3344a60"
   },
   "source": [
    "## 4. Testing the System\n",
    "\n",
    "### Call the A2A agents (the 2 remote agents, and the host agent that refers to the 2 remote agents as sub agents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "03e7b1c16c1a"
   },
   "outputs": [],
   "source": [
    "import json\n",
    "from typing import Any\n",
    "import uuid\n",
    "\n",
    "from a2a.client import A2AClient\n",
    "from a2a.types import (\n",
    "    AgentCard,\n",
    "    MessageSendParams,\n",
    "    SendMessageRequest,\n",
    ")\n",
    "import httpx\n",
    "\n",
    "\n",
    "class A2ASimpleClient:\n",
    "    \"\"\"A2A Simple to call A2A servers.\"\"\"\n",
    "\n",
    "    def __init__(self, default_timeout: float = 240.0):\n",
    "        self._agent_info_cache: dict[str, dict[str, Any] | None] = (\n",
    "            {}\n",
    "        )  # Cache for agent metadata\n",
    "        self.default_timeout = default_timeout\n",
    "\n",
    "    async def create_task(self, agent_url: str, message: str) -> str:\n",
    "        \"\"\"Send a message following the official A2A SDK pattern.\"\"\"\n",
    "        # Configure httpx client with timeout\n",
    "        timeout_config = httpx.Timeout(\n",
    "            timeout=self.default_timeout,\n",
    "            connect=10.0,\n",
    "            read=self.default_timeout,\n",
    "            write=10.0,\n",
    "            pool=5.0,\n",
    "        )\n",
    "\n",
    "        async with httpx.AsyncClient(timeout=timeout_config) as httpx_client:\n",
    "            # Check if we have cached agent card data\n",
    "            if (\n",
    "                agent_url in self._agent_info_cache\n",
    "                and self._agent_info_cache[agent_url] is not None\n",
    "            ):\n",
    "                agent_card_data = self._agent_info_cache[agent_url]\n",
    "            else:\n",
    "                # Fetch the agent card\n",
    "                agent_card_response = await httpx_client.get(\n",
    "                    f\"{agent_url}{AGENT_CARD_WELL_KNOWN_PATH}\"\n",
    "                )\n",
    "                agent_card_data = self._agent_info_cache[agent_url] = (\n",
    "                    agent_card_response.json()\n",
    "                )\n",
    "\n",
    "            # Create AgentCard from data\n",
    "            agent_card = AgentCard(**agent_card_data)\n",
    "\n",
    "            # Create A2A client with the agent card\n",
    "            client = A2AClient(httpx_client=httpx_client, agent_card=agent_card)\n",
    "\n",
    "            # Build the message parameters following official structure\n",
    "            send_message_payload = {\n",
    "                \"message\": {\n",
    "                    \"role\": \"user\",\n",
    "                    \"parts\": [{\"kind\": \"text\", \"text\": message}],\n",
    "                    \"messageId\": uuid.uuid4().hex,\n",
    "                }\n",
    "            }\n",
    "\n",
    "            # Create the request\n",
    "            request = SendMessageRequest(\n",
    "                id=str(uuid.uuid4()), params=MessageSendParams(**send_message_payload)\n",
    "            )\n",
    "\n",
    "            # Send the message with timeout configuration\n",
    "            response = await client.send_message(request)\n",
    "\n",
    "            # Extract text from response\n",
    "            try:\n",
    "                response_dict = response.model_dump(mode=\"json\", exclude_none=True)\n",
    "                if \"result\" in response_dict and \"artifacts\" in response_dict[\"result\"]:\n",
    "                    artifacts = response_dict[\"result\"][\"artifacts\"]\n",
    "                    for artifact in artifacts:\n",
    "                        if \"parts\" in artifact:\n",
    "                            for part in artifact[\"parts\"]:\n",
    "                                if \"text\" in part:\n",
    "                                    return part[\"text\"]\n",
    "\n",
    "                # If we couldn't extract text, return the full response as formatted JSON\n",
    "                return json.dumps(response_dict, indent=2)\n",
    "\n",
    "            except Exception as e:\n",
    "                # Log the error and return string representation\n",
    "                print(f\"Error parsing response: {e}\")\n",
    "                return str(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a2a_client = A2ASimpleClient()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "a8a1d8d5055d"
   },
   "outputs": [],
   "source": [
    "trending_topics = await a2a_client.create_task(\n",
    "    \"http://localhost:10020\", \"What's trending today?\"\n",
    ")\n",
    "print(trending_topics)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "997a99f1b9ab"
   },
   "outputs": [],
   "source": [
    "analysis = await a2a_client.create_task(\n",
    "    \"http://localhost:10021\", \"Analyze the trend AI in Social Media\"\n",
    ")\n",
    "print(analysis)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "524a44a8cb18"
   },
   "outputs": [],
   "source": [
    "host_analysis = await a2a_client.create_task(\n",
    "    \"http://localhost:10022\",\n",
    "    \"Find the most relevant trends in the web today, choose randomly one of the top trends, and give me a complete analysis of it with quantitative data\",\n",
    ")\n",
    "print(host_analysis)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dc0dcc1fdce1"
   },
   "source": [
    "## Summary\n",
    "\n",
    "Congratulations! You've successfully built a multi-agent system using Google's A2A protocol. Here's what you've learned:\n",
    "\n",
    "1. **A2A Protocol Basics**: How agents discover and communicate with each other\n",
    "2. **ADK Integration**: Creating ADK agents and wrapping them for A2A\n",
    "3. **Agent Orchestration**: Building a Host Agent that coordinates multiple agents\n",
    "4. **Practical Implementation**: Running and testing a complete multi-agent system\n",
    "\n",
    "### Next Steps\n",
    "\n",
    "- **Deploy Your Agents**: Deploy agents to Cloud Run or other platforms\n",
    "- **Add Authentication**: Implement security for production use\n",
    "- **Create More Agents**: Build agents for your specific use cases, even using other frameworks\n",
    "- **Advanced Patterns**: Explore agent chains, parallel execution, and more\n",
    "- **Callbacks**: Add in the Google ADK agents the before and after callbacks of the agent, model and tool, to increase observability\n",
    "\n",
    "Happy agent building! 🚀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "13f0c7f08d25"
   },
   "source": [
    "# Appendix\n",
    "\n",
    "### Why Use Google A2A (Agent-to-Agent) Protocol\n",
    "\n",
    "Google's Agent-to-Agent (A2A) protocol is a standardized communication framework that enables AI agents to discover, communicate, and collaborate with each other using a common JSON-RPC based protocol.  \n",
    "It provides a uniform way for agents to interact, regardless of their underlying implementation.  \n",
    "\n",
    "#### 1. Standardized Communication Protocol\n",
    "\n",
    "- A2A provides a consistent, JSON-RPC based protocol that any agent can implement\n",
    "- Agents can communicate without needing to know each other's internal implementation details\n",
    "- The protocol supports streaming responses for real-time interactions\n",
    "\n",
    "#### 2. Agent Discovery and Metadata\n",
    "\n",
    "- Agents expose their capabilities through standardized metadata (AgentCard)\n",
    "- Each agent publishes its skills, input/output modes, and capabilities\n",
    "- Host agents can dynamically discover what other agents can do through the `.well-known/agent.json` endpoint\n",
    "\n",
    "#### 3. Orchestration and Composition\n",
    "\n",
    "- Enables building complex multi-agent systems where a host agent can orchestrate multiple specialized agents\n",
    "- Supports sequential and parallel task execution patterns\n",
    "- Allows for sophisticated agent collaboration workflows\n",
    "\n",
    "#### 4. Platform Independence\n",
    "\n",
    "- A2A servers can wrap agents from different frameworks (not just ADK)\n",
    "- Agents can be deployed as independent services on different infrastructure\n",
    "- Promotes loose coupling between agents\n",
    "\n",
    "### Differences: Using ADK Agents Directly vs. Through A2A\n",
    "\n",
    "#### Using ADK Agents Directly\n",
    "\n",
    "```python\n",
    "# Conceptual Example: Defining Hierarchy\n",
    "from google.adk.agents import LlmAgent, BaseAgent\n",
    "\n",
    "# Define individual agents\n",
    "greeter = LlmAgent(name=\"Greeter\", model=\"gemini-2.5-pro\")\n",
    "task_doer = BaseAgent(name=\"TaskExecutor\")  # Custom non-LLM agent\n",
    "\n",
    "# Create parent agent and assign children via sub_agents\n",
    "coordinator = LlmAgent(\n",
    "    name=\"Coordinator\",\n",
    "    model=\"gemini-2.5-pro\",\n",
    "    description=\"I coordinate greetings and tasks.\",\n",
    "    sub_agents=[greeter, task_doer],  # Assign sub_agents here\n",
    ")\n",
    "```\n",
    "\n",
    "__Use Direct ADK for Multi-Agents System When:__\n",
    "\n",
    "- All agents are tightly related and always used together\n",
    "- Google ADK is the framework choice, and simplicity is prioritized\n",
    "- Performance of in-process communication is critical\n",
    "- You don't need distributed deployment\n",
    "- No built-in service discovery is needed\n",
    "\n",
    "#### Using ADK Agents Through A2A\n",
    "\n",
    "__Use A2A for Multi-Agents System When:__\n",
    "\n",
    "- Building complex multi-agent systems\n",
    "- Agents need to be developed, deployed, and scaled independently\n",
    "- You want to integrate agents from different teams or frameworks\n",
    "- You need dynamic agent discovery and composition\n",
    "- Building a platform where agents can be added/removed dynamically\n",
    "- You want to enable third-party agent integration"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "a2a_quickstart.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
